from dataclasses import dataclass
from typing import List

from PySide2 import QtGui
from PySide2.QtCore import Qt
from PySide2.QtGui import QColor, QPalette
from PySide2.QtWidgets import QTableWidget, QAbstractItemView, QHeaderView, QTableWidgetItem
import pandas as pd

from .pd import get_df_from_db
from .qtwgt import Model_Combobox, ButtonWithLink, ButtonFun


@dataclass
class ColItem:
    col_name: str = None  # 列名
    col_nickname: str = None  # 别名（显示名称）
    widget_type: str = "item"  #
    value_attr_name: str = None
    is_hide: bool = False  # 是否隐藏
    is_edit: bool = False  # 是否可编辑
    col_value: object = None
    col_fun: object = None
    col_width: int = 0  # 列宽


# region 表格控件处理
def add_to_table(table: QTableWidget, model_item: ColItem, value, cell):
    if model_item.widget_type == "item":
        add_model_label(table, model_item, cell, value)
    elif model_item.widget_type == "cmb":
        add_model_cmb(table, model_item, cell, value)
    elif model_item.widget_type == "btn":
        add_model_btn(table, model_item, cell, value)
    elif model_item.widget_type == "btn_fun":
        add_model_fun_btn(table, model_item, cell, value)


def add_model_cmb(table: QTableWidget, model_item: ColItem, cell, value):
    """
    para: {option_list,model,change_func}
    """

    if model_item.col_value:
        options = model_item.col_value
    else:
        options = ["所有"]
    q_cmb = Model_Combobox()
    q_cmb.col_name = model_item.col_name
    q_cmb.row_index = cell[0]
    q_cmb.addItems(options)
    if model_item.col_fun:
        q_cmb.currentTextChanged.connect(lambda text: model_item.col_fun(text))
    else:
        q_cmb.currentTextChanged.connect(
            lambda text: table.add_value_to_change_dic(text, q_cmb.row_index, model_item.value_attr_name))

    if value and value in options:
        q_cmb.setCurrentText(value)
        if not table.is_original:
            palette = QPalette()
            palette.setColor(QPalette.Text, QColor("blue"))

    table.setCellWidget(*cell, q_cmb)


def add_model_btn(table: QTableWidget, btn_item: ColItem, cell, value):
    if btn_item.col_value:
        btn_name = btn_item.col_value
    else:
        btn_name = btn_item.col_nickname
    if btn_item.col_fun:
        value = btn_item.col_fun(value)
    btn = ButtonWithLink(btn_name, value)
    table.setCellWidget(*cell, btn)


def add_model_fun_btn(table: QTableWidget, btn_item: ColItem, cell, value):
    if btn_item.col_value:
        btn_name = btn_item.col_value
    else:
        btn_name = btn_item.col_nickname
    btn = ButtonFun(btn_name, btn_item.col_fun, (value,))
    table.setCellWidget(*cell, btn)


def add_model_label(table, lb_item: ColItem, cell, value):
    table.setItem(*cell, QTableWidgetItem(str(value)))
    table_item: QTableWidgetItem = table.item(*cell)
    if not lb_item.is_edit:
        set_item_no_edit(table_item)


def set_item_no_edit(wtg_item: QTableWidgetItem):
    """设置单元格是不可编辑"""

    if not wtg_item:
        assert False, "没有控件"
    editable = False
    wtg_item.setFlags(wtg_item.flags() & ~Qt.ItemIsEditable) if not editable else wtg_item.setFlags(
        wtg_item.flags() | Qt.ItemIsEditable)
    # 设置为不可编辑
    wtg_item.setData(Qt.ForegroundRole, QColor('black'))  # 文本颜色
    wtg_item.setData(Qt.BackgroundRole, QColor(240, 240, 240))  # 背景色


# endregion

class DFTable(QTableWidget):
    """功能：
        1、展示数据到QTableWidget
        2、记录修改的数据
        3、保存修改数据至数据库
    """

    def __init__(self, tb_name, col_model_list, key_col_name="id", find_condition=None, merge_key_col_name=None):
        super().__init__()
        # 数据表的信息
        self.key_col_index = None
        self.tb_name = tb_name
        self.col_name_list = []  # 列名列表
        self.col_name_dic = dict()  # 列名字典
        self.col_model_list = col_model_list  # 数据列的控件对象列表

        self.find_condition = find_condition

        # 表头相关信息
        self.key_col_name = key_col_name  # 用于查询唯一数据的关列名
        self.set_col_header()

        # 关键列的列位置
        self.datas_key_col_value_list = []
        self.col_header_names = []  # 表头名

        # 表数据相关
        self.datas: pd.DataFrame = pd.DataFrame()  # 数据
        self.datas_show = pd.DataFrame()  # 要展示的对象列表
        self.need_merge_col_name_list: list = []  # 需要合并的列名
        self.merge_key_col_name = merge_key_col_name

        self.is_original = True  # 是否是初始状态

        self.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.setEditTriggers(QAbstractItemView.DoubleClicked)

        self.itemChanged.connect(self.item_changed)
        self.change_dic = dict()
        self.change_color = "blue"

    def __get_col_model_dic(self) -> None:
        """
        根据列模型信息设置列的标签名、列模型字典等信息
        """
        col_model_list = self.col_model_list
        if col_model_list:
            self.col_name_list = []
            self.col_name_dic = dict()
            self.col_header_names = []
            for com_model in col_model_list:
                self.col_name_list.append(com_model.col_name)
                self.col_name_dic[com_model.col_name] = com_model
                self.col_header_names.append(com_model.col_nickname)

    def set_col_header(self):
        """
        设置列名，列是否隐藏、列宽等信息
        """
        self.__get_col_model_dic()
        if self.col_header_names:
            self.setColumnCount(len(self.col_header_names))
            self.setHorizontalHeaderLabels(self.col_header_names)
            col_name_list = self.col_name_list
            for index, col_name in enumerate(col_name_list):
                col_model: ColItem = self.col_name_dic[col_name]
                self.setColumnHidden(index, col_model.is_hide)
                if not col_model.is_hide and col_model.col_width:
                    self.setColumnWidth(index, col_model.col_width)
        self.key_col_index = self.col_name_list.index(self.key_col_name)

    def init_datas(self, datas):
        """根据获取的数据，并重置表格数据"""
        self.datas = datas
        self.datas_show = datas
        self.datas_key_col_value_list = datas[self.key_col_name].tolist()
        self.__insert_rows()

    # region model和df显示数据的方法及无数据的显示信息

    def __show_df_rows(self):
        self.is_original = True
        self.setRowCount(0)
        df_show_data: pd.DataFrame = self.datas_show
        row_count, col_count = df_show_data.shape
        if not row_count:
            self.no_data_show()
        self.setRowCount(row_count)
        self.__show_merge_df_rows(df_show_data)
        self.is_original = False

    def __show_merge_df_rows(self, df_show_data):
        row_index = 0
        if self.merge_key_col_name:
            merge_col_list = df_show_data[self.merge_key_col_name].unique().tolist()

            for merge_value in merge_col_list:
                df_merge: pd.DataFrame = df_show_data[df_show_data[self.merge_key_col_name]==merge_value]
                row_index = self.add_cell_to_table(df_merge, row_index)
        else:
            self.add_cell_to_table(df_show_data, row_index)

    def add_cell_to_table(self, df_merge, row_index):
        row_count, col_count = df_merge.shape
        for col_num, col in enumerate(self.col_name_list):
            col_model: ColItem = self.col_name_dic[col]

            if not col_model.value_attr_name:
                col_model.value_attr_name = col_model.col_name
            df_col_name = col_model.value_attr_name

            if "date" in df_col_name:
                df_merge[col] = pd.to_datetime(df_merge[df_col_name]).dt.date
            col_ser = df_merge[df_col_name]
            if row_count > 1 and df_col_name in self.need_merge_col_name_list:
                value = col_ser[0]
                self.setSpan(row_index, col_num, row_count, 1)
                cell = (row_index, col_num)  # 形成行数和列数
                add_to_table(self, col_model, value, cell)
            else:
                for index, value in enumerate(col_ser.values):
                    cell = (row_index + index, col_num)  # 形成行数和列数
                    add_to_table(self, col_model, value, cell)
            # 设置表格宽度值
            if col_model.col_width:
                self.setColumnWidth(col_num, col_model.col_width * 17)
            else:
                if not col_model.is_hide:
                    self.resizeColumnToContents(col_num)

        return row_index + row_count

    def __set_cell(self, cell_value):
        pass

    def __insert_rows(self):
        self.is_original = True
        self.__show_df_rows()
        self.is_original = False

    def no_data_show(self):
        """无数据展示"""
        self.setRowCount(1)
        self.setSpan(0, 2, len(self.col_header_names) - 3, 1)
        self.setItem(0, 2, QTableWidgetItem("未查询到相关数据"))

    # 显示数据至表格

    # endregion

    # 重置数据
    def reset_data(self):
        datas = get_df_from_db(self.tb_name, self.find_condition)
        self.init_datas(datas)

    def show_df_data(self, show_data_df: pd.DataFrame):
        self.datas_show = show_data_df
        self.__insert_rows()

    # region 记录单元格的值变化
    def item_changed(self, item: QTableWidgetItem):
        """单元格数据变化的处理
            处理思路： 获取单元格的行和列，即获取行的key_col_name的值，以及单元格的列名
            数据的key_col_name的值作为字典的key, k中包含一个字典 col_name :value
        """
        if not self.is_original:
            item.setForeground(QtGui.QBrush(QtGui.QColor(self.change_color)))
            value = item.text()
            row_mun = item.row()
            col_num = item.column()
            col_name = self.col_name_list[col_num]
            self.add_value_to_change_dic(value, row_mun, col_name)

    def add_value_to_change_dic(self, value, row_index, col_name):
        """把改变的数据放入改变数据的字典中"""
        if not self.is_original:
            key_value = self.item(row_index, self.key_col_index).text()
            if self.key_col_name == "id":
                key_value = int(key_value)

            if key_value in self.change_dic.keys():
                self.change_dic[key_value][col_name] = value
            else:
                self.change_dic[key_value] = {col_name: value}
            # print(f"已修改第{row_index}行的{col_name}值为:{value}")
            # print("已修改的值为：",self.change_dic)

    # endregion
    # 获取单元格
    def get_cell_widget(self, row, column):
        """获取单元格的控件"""
        return self.cellWidget(row, column)

    # region 获取所选的数据信息
    def get_selected_rows(self):
        datas = self.datas
        if self.key_col_name:
            select_key_list = self.get_selected_key_values()
            selected_df = datas.loc[datas[self.key_col_name].isin(select_key_list)]
            return selected_df

    def get_selected_key_values(self):
        """根据关键列名，则返回所选ehh的关键列的的数据，若无则返回行序号"""
        selected_cells = self.selectedItems()
        # 提取行索引
        selected_rows = set()
        for cell in selected_cells:
            selected_rows.add(cell.row())
        if self.key_col_name:
            col_index = self.col_name_list.index(self.key_col_name)
            select_data_list = []
            for row_index in selected_rows:
                data = self.item(row_index, col_index).text()
                if self.key_col_name == "id":
                    data = int(data)
                select_data_list.append(data)
            return select_data_list
        return selected_rows

    # endregion
